#ifndef FIELD_HPP
#define FIELD_HPP

#include "meshTypes_fwd.hpp"
#include "fieldBase.hpp"
#include "solverBaseTypes_fwd.hpp"
#include "boundaryCondition.hpp"
#include <experimental/memory>

namespace hms {


template<typename _MeshType>
class FieldTraits<Field<_MeshType>> {
public:
	using MeshType = _MeshType;
	using ValueType = ArrayXXs&;
	using ConstValueType = const ArrayXXs&;
};


template<typename _MeshType>
class Field : public FieldBase<Field<_MeshType>>
{
public:
	using MeshType = _MeshType;
	using Type = Field<MeshType>;
	using ValueType = typename FieldTraits<Type>::ValueType;
	using ConstValueType = typename FieldTraits<Type>::ConstValueType;

private:
	ArrayXXs m_values;
	std::vector<std::unique_ptr<BoundaryCondition<MeshType>>>
		m_boundaryConditions;

public:
	Field() = default;
	Field( const MeshBase<MeshType>&, Index nComponents );
	Field( const MeshBase<MeshType>&, ArrayXXs&& values );

	/* make sure only move assignment and construction are used */
	Field( Field&& ) = default;
	Field& operator=( Field&& ) = default;
	
	Field( const Field& ) = delete;
	Field( Field& ) = delete;

	/* don't hide the inherited function, but provide an overload */
	using FieldBase<Field<MeshType>>::domainValues;
	auto domainValues( const BoundaryPatch& ) const -> decltype(auto);
	auto domainValues( const BoundaryPatch& ) -> decltype(auto);
	
	auto ghostValues( const BoundaryPatch& ) const -> decltype(auto);
	auto ghostValues( const BoundaryPatch& ) -> decltype(auto);

	template<typename BCType, typename ... Ts>
	auto setBoundaryCondition( const BoundaryPatch&, Ts&& ... args )
		-> BCType&;

	template<typename BCType>
	auto setBoundaryCondition( BCType&& bc )
		-> BCType&;

	auto boundaryConditions() const ->
		const std::vector<std::unique_ptr<BoundaryCondition<MeshType>>>&;
	
	auto boundaryCondition( const BoundaryPatch& ) const ->
		const BoundaryCondition<MeshType>&;
	
	auto boundaryCondition( const std::string& patch ) const ->
		const BoundaryCondition<MeshType>&;
	
	bool checkBoundaryConditions() const;

private:
	auto valuesImp() const -> ConstValueType;
	auto valuesImp() -> ValueType;

	auto domainValuesImp() -> decltype(auto);
	auto domainValuesImp() const -> decltype(auto);

	void resizeBCsToPatchesSize();

	friend FieldBase<Field<MeshType>>;
};

}

#include "field.tpp"

#endif